//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Base/Include/Common.h"
#include "Base/Platform/ScreenCoordTypes.h"
#include "Base/Utils/Locale/LocaleUtils.h"

namespace Engine
{
namespace Base
{

	struct Device;


	//
	// Display
	//
	
	struct Display
	{
		friend struct DeviceEmulator;
		friend class Engine::Platforms::IPlatform;

	// types
	public:
		enum EOrientation
		{
			ORIENT_0	= 0,
			ORIENT_90	= 1,
			ORIENT_180	= 2,
			ORIENT_270	= 3,
			_ORIENTS_COUNT
		};

		typedef ScreenCoords::Dip		DipCoord;
		typedef ScreenCoords::Physics	PhysicsCoord;
		typedef ScreenCoords::Pixels	PixelsCoord;
		typedef ScreenCoords::SNorm		SNormCoord;
		typedef ScreenCoords::UNorm		UNormCoord;

		struct SetterGetter
		{
			static void  SetResolution (Display &disp, const PixelsCoord &res);
			static void  SetResolution (Device &dev, const PixelsCoord &res);

			static PixelsCoord GetResolution (const Display &disp);

			static void  SetOrientation (Display &disp, EOrientation orient);
		};


	// variables
	protected:
		PixelsCoord		_res;		// screen resolution in pixels
		PhysicsCoord	_size;		// physical size in meters
		uint			_ppi;		// pixels per inch
		EOrientation	_orient;


	// methods
	public:
		Display () : _orient(ORIENT_0), _ppi(0) {}

		ivec2			Resolution ()					const	{ return SetterGetter::GetResolution( *this ).Get(); }
		vec2 const &	PhysicsSize ()					const	{ return _size.Get(); }
		EOrientation	Orientation ()					const	{ return _orient; }
		uint			PPI ()							const	{ return _ppi; }
		float			AspectRatio ()					const	{ return _res.x / (float)_res.y; }

		bool			IsHorizontal ()					const	{ return _res.x > _res.y; }
		bool			IsVertical ()					const	{ return not IsHorizontal(); }

		// calculation
		PhysicsCoord	CalcPhysicsSize ()				const	{ return PhysicsCoord( _res.Get().To<vec2>() / float(_ppi) * 25.4f ); }
		float			CalcPPmm ()						const	{ return Pythagoras::Diagonal( (float)_res.x, (float)_res.y ) /
																		 Pythagoras::Diagonal( (float)_size.x, (float)_size.y ); }
		float			CalcPpi ()						const	{ return CalcPPmm() / Square( 25.4f ); }
		DipCoord		CalcDip ()						const	{ return ToDip( _res ); }

		// converter
		DipCoord		ToDip (const PixelsCoord &c)	const	{ return DipCoord( c.Get().To<vec2>() * ( 160.0f / float(_ppi) ) ); }
		DipCoord		ToDip (const PhysicsCoord &c)	const	{ return ToDip( ToPixels( c ) ); }
		DipCoord		ToDip (const SNormCoord &c)		const	{ return ToDip( ToPixels( c ) ); }
		DipCoord		ToDip (const UNormCoord &c)		const	{ return ToDip( ToPixels( c ) ); }

		PhysicsCoord	ToPhysics (const DipCoord &c)	const	{ return ToPhysics( ToUNorm( c ) ); }
		PhysicsCoord	ToPhysics (const PixelsCoord &c)const	{ return ToPhysics( ToUNorm( c ) ); }
		PhysicsCoord	ToPhysics (const SNormCoord &c)	const	{ return ToPhysics( UNormCoord( c ) ); }
		PhysicsCoord	ToPhysics (const UNormCoord &c)	const	{ return PhysicsCoord( c.Get() * _size.Get() ); }

		PixelsCoord		ToPixels (const DipCoord &c)	const	{ return PixelsCoord( Round<int>( c.Get() * ( float(_ppi) / 160.0f ) ) ); }
		PixelsCoord		ToPixels (const PhysicsCoord &c)const	{ return ToPixels( ToUNorm( c ) ); }
		PixelsCoord		ToPixels (const SNormCoord &c)	const	{ return ToPixels( UNormCoord( c ) ); }
		PixelsCoord		ToPixels (const UNormCoord &c)	const	{ return PixelsCoord( Round<int>( c.Get() * _res.Get().To<vec2>() ) ); }

		UNormCoord		ToUNorm (const DipCoord &c)		const	{ return ToUNorm( ToPixels( c ) ); }
		UNormCoord		ToUNorm (const PhysicsCoord &c)	const	{ return UNormCoord( c.Get() / _size.Get() ); }
		UNormCoord		ToUNorm (const PixelsCoord &c)	const	{ return UNormCoord( c.Get().To<vec2>() / _res.Get().To<vec2>() ); }
		UNormCoord		ToUNorm (const SNormCoord &c)	const	{ return UNormCoord( c ); }

		SNormCoord		ToSNorm (const DipCoord &c)		const	{ return SNormCoord( ToUNorm( c ) ); }
		SNormCoord		ToSNorm (const PhysicsCoord &c)	const	{ return SNormCoord( ToUNorm( c ) ); }
		SNormCoord		ToSNorm (const PixelsCoord &c)	const	{ return SNormCoord( ToUNorm( c ) ); }
		SNormCoord		ToSNorm (const UNormCoord &c)	const	{ return SNormCoord( c ); }

		PixelsCoord		Clamp (const PixelsCoord &c)	const	{ return PixelsCoord( UXMath::Clamp( c.Get(), ivec2(0), _res.Get() ) ); }
		PhysicsCoord	Clamp (const PhysicsCoord &c)	const	{ return PhysicsCoord( UXMath::Clamp( c.Get(), vec2(0), _size.Get() ) ); }
		DipCoord		Clamp (const DipCoord &c)		const	{ return DipCoord( UXMath::Clamp( c.Get(), vec2(0), CalcDip().Get() ) ); }
		SNormCoord		Clamp (const SNormCoord &c)		const	{ return SNormCoord( UXMath::Clamp( c.Get(), vec2(-1.0f), vec2(1.0f) ) ); }
		UNormCoord		Clamp (const UNormCoord &c)		const	{ return UNormCoord( UXMath::Clamp( c.Get(), vec2(0), vec2(1.0f) ) ); }
	};



	//
	// Device
	//

	struct Device
	{
		friend struct DeviceEmulator;
		friend struct Display::SetterGetter;
		friend class Engine::Platforms::IPlatform;

	// types
	public:
		// TODO: create locale class and remove this types
		typedef LocaleUtils::iso2_t		iso2_t;
		typedef LocaleUtils::iso3_t		iso3_t;


	// varibales
	protected:
		Display			_display;

		string			_name;
		string			_os;
		string			_gpu;
		string			_package;
		string			_deviceID;

		iso2_t			_iso2Locale;
		iso3_t			_iso3Locale;

		uint			_numCPUs;
		uint			_cpuFreq;			// MHz
		Bytes			_totalMem;

		bool			_hardwareKeyboard;
		bool			_keyboardOpen;


	// methods
	public:
		Device () :
			_numCPUs(1), _cpuFreq(0), _totalMem(0), _deviceID(0),
			_hardwareKeyboard(false), _keyboardOpen(false)
		{}

		string const &		GetName ()				const	{ return _name; }
		string const &		GetOsName ()			const	{ return _os; }
		string const &		GetDeviceID ()			const	{ return _deviceID; }
		string const &		GetGpuName ()			const	{ return _gpu; }
		string const &		GetPackage ()			const	{ return _package; }

		uni_c_string		GetIso2Locale ()		const	{ return uni_c_string( _iso2Locale.ptr(), 2 ); }
		uni_c_string		GetIso3Locale ()		const	{ return uni_c_string( _iso3Locale.ptr(), 3 ); }

		Display const &		GetDisplay ()			const	{ return _display; }
		
		uint				NumberOfCPUs ()			const	{ return _numCPUs; }
		uint				CpuFrequency ()			const	{ return _cpuFreq; }
		Bytes				TotalMemory ()			const	{ return _totalMem; }
		bool				HasHardwareKeyboard ()	const	{ return _hardwareKeyboard; }
		bool				IsHWKeyboardOpen ()		const	{ return _keyboardOpen; }

		void WriteToLog ()
		{
			string	s;

			s	<< "Package: " << _package
				<< "\nDevice: " << _name
				<< "\nOS: " << _os
				<< "\nID: " << _deviceID
				<< "\nCPU: " << _cpuFreq
				<< " MHz x" << _numCPUs
				<< "\nGPU: " << _gpu
				<< "\nRAM: " << TotalMemory().ToString()
				<< "\nDisplay: " << _display.Resolution().x << 'x' << _display.Resolution().y
				<< "\nPPI: " << _display.PPI()
				<< "\norient: ";

			switch ( _display.Orientation() )
			{
				case Display::ORIENT_0   :	s << "portrait (0)";			break;
				case Display::ORIENT_90  :	s << "landscape (90)";			break;
				case Display::ORIENT_180 :	s << "reverse portrait (180)";	break;
				case Display::ORIENT_270 :	s << "reverse landscape (270)";	break;
			}

			LOG( s.cstr(), ELog::INFO );
		}
	};


}	// Base
}	// Engine